﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace CS4730_XNA_Graphics_Tutorial.Lights {

    public interface Light {
        void Prepare(Effect SimpleEffect);
    }

    public class PhongEffect {

        private Effect SimpleEffect;

        private List<Light> lights = new List<Light>();
        public List<Light> Lights { get { return lights; } }

        public Matrix View { get { return SimpleEffect.Parameters["View"].GetValueMatrix(); } set { SimpleEffect.Parameters["View"].SetValue(value); } }
        public Matrix Projection { get { return SimpleEffect.Parameters["Projection"].GetValueMatrix(); } set { SimpleEffect.Parameters["Projection"].SetValue(value); } }
        public Matrix World {
            get {
                return SimpleEffect.Parameters["World"].GetValueMatrix();
            }
            set {
                SimpleEffect.Parameters["World"].SetValue(value);

                Matrix linear = value;
                linear.Translation = Vector3.Zero;
                SimpleEffect.Parameters["WorldNormal"].SetValue(Matrix.Invert(Matrix.Transpose(linear)));
            }
        }

        public Vector3 ViewerLocation { get { return SimpleEffect.Parameters["vViewerLocation"].GetValueVector3(); } set { SimpleEffect.Parameters["vViewerLocation"].SetValue(value); } }
        public Vector3 MaterialDiffuse { get { return SimpleEffect.Parameters["cMaterialDiffuse"].GetValueVector3(); } set { SimpleEffect.Parameters["cMaterialDiffuse"].SetValue(value); } }
        public Vector3 MaterialSpecular { get { return SimpleEffect.Parameters["cMaterialSpecular"].GetValueVector3(); } set { SimpleEffect.Parameters["cMaterialSpecular"].SetValue(value); } }
        public float MaterialSpecularFalloff { get { return SimpleEffect.Parameters["MaterialSpecularFalloff"].GetValueSingle(); } set { SimpleEffect.Parameters["MaterialSpecularFalloff"].SetValue(value); } }
        public Texture2D Texture { get { return SimpleEffect.Parameters["Texture"].GetValueTexture2D(); } set { SimpleEffect.Parameters["Texture"].SetValue(value); } }


        public void Render(Renderable renderObject, GameTime gameTime) {
            SimpleEffect.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
            SimpleEffect.GraphicsDevice.BlendState = BlendState.Opaque;

            SimpleEffect.CurrentTechnique = SimpleEffect.Techniques["RenderBlack"];
            renderObject.render(gameTime, SimpleEffect);

            SimpleEffect.GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
            SimpleEffect.GraphicsDevice.BlendState = BlendState.Additive;

            foreach (Light effect in lights) {
                effect.Prepare(SimpleEffect);
                renderObject.render(gameTime, SimpleEffect);
            }

            SimpleEffect.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
            SimpleEffect.GraphicsDevice.BlendState = BlendState.Opaque;
        }


        public PhongEffect(Effect effect) {
            SimpleEffect = effect;
        }

    }

    public class PhongAmbientLight : Light {

        public Vector3 Color { get; set; }

        public void Prepare(Effect SimpleEffect) {
            SimpleEffect.Parameters["cAmbientLightColor"].SetValue(Color);
            SimpleEffect.CurrentTechnique = SimpleEffect.Techniques["TexturedAmbient"];
        }
    }

    public class PhongDirectionalLight : Light {

        public Vector3 Direction { get; set; }
        public Vector3 Color { get; set; }

        public void Prepare(Effect SimpleEffect) {
            SimpleEffect.Parameters["uDirectionalLightDirection"].SetValue(Vector3.Normalize(Direction));
            SimpleEffect.Parameters["cDirectionalLightColor"].SetValue(Color);

            SimpleEffect.CurrentTechnique = SimpleEffect.Techniques["TexturedDirectional"];
        }
    }

    public class PhongPointLight : Light {

        public Vector3 Position { get; set; }
        public Vector3 Color { get; set; }
        public Vector3 Attenuation { get; set; }

        public void Prepare(Effect SimpleEffect) {
            SimpleEffect.Parameters["vPointLightPosition"].SetValue(Position);
            SimpleEffect.Parameters["cPointLightColor"].SetValue(Color);
            SimpleEffect.Parameters["dPointLightAttenuation"].SetValue(Attenuation);

            SimpleEffect.CurrentTechnique = SimpleEffect.Techniques["TexturedPoint"];
        }
    }

    public class PhongSpotLight : Light {

        public Vector3 Position { get; set; }
        public Vector3 Color { get; set; }
        public Vector3 Attenuation { get; set; }
        public Vector3 Direction { get; set; }
        public float Dropoff { get; set; }
        public float Cutoff { get; set; }

        public void Prepare(Effect SimpleEffect) {
            SimpleEffect.Parameters["vSpotLightPosition"].SetValue(Position);
            SimpleEffect.Parameters["cSpotLightColor"].SetValue(Color);
            SimpleEffect.Parameters["dSpotLightAttenuation"].SetValue(Attenuation);
            SimpleEffect.Parameters["uSpotLightDirection"].SetValue(Vector3.Normalize(Direction));
            SimpleEffect.Parameters["SpotLightDropoff"].SetValue(Dropoff);
            SimpleEffect.Parameters["SpotLightCutoff"].SetValue(Cutoff);

            SimpleEffect.CurrentTechnique = SimpleEffect.Techniques["TexturedSpot"];
        }
    }
}
